package web.api.global.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import web.common.core.exception.ErrorCode;
import web.common.core.exception.WebCodeException;
import web.common.core.util.StringUtil;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author Jin Zheng
 * @since 1.0 2022-01-26
 */
@RestControllerAdvice
public class ControllerExceptionHandler extends ResponseEntityExceptionHandler {
	private static Logger logger = LoggerFactory.getLogger(ControllerExceptionHandler.class);

	public ControllerExceptionHandler() {
	}

	@ExceptionHandler(WebCodeException.class)
	public ResponseEntity<Object> handleWebCodeException(WebCodeException ex, WebRequest request) {
		HttpStatus status = HttpStatus.resolve(ex.getHttpStatus());
		if (status == null) {
			status = HttpStatus.INTERNAL_SERVER_ERROR;
		}
		String error = ex.toJsonString();
		HttpHeaders headers = new HttpHeaders();
		logger.error("WebCodeException: {}", ex.getMessage());
		return handleExceptionInternal(ex, error, headers, status, request);
	}

	@ExceptionHandler(Exception.class)
	public ResponseEntity<Object> handleException(Exception ex) {
		HttpHeaders headers = new HttpHeaders();
		String error = ErrorCode.ERROR.toJsonString();
		logger.error("Exception: {}", ex.getMessage(), ex);
		return handleExceptionInternal(ex, error, headers, HttpStatus.INTERNAL_SERVER_ERROR, null);
	}

	@Override
	protected ResponseEntity<Object> handleBindException(
			BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
		String error = this.getBindErrorResponse(ex);
		return handleExceptionInternal(ex, error, headers, status, request);
	}

	@Override
	protected ResponseEntity<Object> handleMethodArgumentNotValid(
			MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
		String error = this.getBindErrorResponse(ex);
		return handleExceptionInternal(ex, error, headers, status, request);
	}

	private String getBindErrorResponse(BindingResult bindingResult) {
		List<FieldError> errors = bindingResult.getFieldErrors();
		String errorStr = StringUtil.join(errors.stream()
				.map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.toList()),
				",");
		logger.warn("BindException: {}", errorStr);
		return ErrorCode.INVALID_PARAM.toJsonString(errorStr);
	}

}
